﻿using System;
using System.Collections.Generic;
using System.Linq;
using TSymbols = System.Collections.Generic.List<myLittleC.ISymbol>;

namespace myLittleC
{
    class Symbol : ISymbol
    {
        public static String OneShift { get { return "  "; } }

        public virtual String Name { get; protected set; }
        public virtual String ToString(String shift) { return shift + Name + "\n"; }
	}

    class SymType : Symbol
    {

        public virtual Int32 Size { get; set; }
        protected SymType():this("", 0) {}
        protected SymType(String iName) : this(iName, 0) { }

        protected SymType(String iName, Int32 iSize)
        {
            Size = iSize;
            Name = iName;
        }

        public override string ToString(String shift)
        {   return  String.Format("{0}{1}  {2}", shift, Name, Size);}
    }

    class SymTypeInt : SymType
    {
        public SymTypeInt() : base("int", 4) { }
    }

    class SymTypeFloat : SymType
    {
        public SymTypeFloat() : base("float", 4) { }
    }

    class SymTypeCharsConst : SymType 
    {
        public SymTypeCharsConst() : base("charsconsts", 1) { }
    }

    sealed class SymStruct :SymType
    {
        public SymbolTable Items { get; set; }
        public String Tag { get { return Name; } }

        public SymStruct() : this("-noTag") { }
        public SymStruct(String iName) { Name = iName;}

        public bool IsPrototipe { get { return Items == null; } }

        public override Int32 Size
        {
            get {   return Items.Vareables.Cast<SymVar>().Sum(t => t.Type.Size);}
        }

        public override string ToString(String shift)
        {
            return String.Format("{0}Struct {1} Size: {2}\n{3}\n{0}End Struct\n", shift, Name, Size, Items.ToString(shift + OneShift));
        }
    }

    class SymTypedef : SymType
    {
        public SymType Type { get; private set; }

        public SymTypedef(String iName, SymType iType) :base(iName) 
        {   Type = iType;}

        public override string ToString(string shift)
        { return String.Format("{0}{1}, type: {2}", shift, Name, Type.Name); }
    }

    //TODO: сделать масив наследником указателя
    class SymTypeLink : SymType
    {
        public SymType Type { get; private set; }

        public SymTypeLink(SymType iType) : base("*"+iType.Name, iType.Size)
        {
            Type = iType;
        }

        public override int Size    {   get {   return 4;}}
    }

    class SymTypeArray : SymType
    {
        public SymType Type { get; private set; }
        public SymTypeArray(SymType iType, ref List<UInt64> iSize)
            : base(iType.Name, iType.Size) 
        { 
            Lenght = iSize;
            Type = iType;

            SumLenght = 1;
            foreach (var len in Lenght)
                SumLenght *= len;
        }

        public UInt64 SumLenght { get; private set; }

        public List<UInt64> Lenght { get; private set; }

    }


    class SymVar :Symbol, ISymbol
    {
        public SymType Type { get; private set; }        
        public SyntaxExpression Value { get; set; }
        private bool Global { get; set; }
        public bool IsFuncArg { get; private set; }


        public SymVar(String iName, SymType iType):this(iName, iType, false) { }

        protected SymVar(String iName, SymType iType, bool iGlobal) 
        {
            Name = iName;
            Type = iType;
            Global = iGlobal;
        }

        public SymVar(String iName, SymType iType, bool iGlobal, bool funkArgs)
        {
            Name = iName;
            Type = iType;
            Global = iGlobal;
            IsFuncArg = funkArgs;
        }

        public override string ToString(String shift)
        {
            string s = ((Value != null) ? "Value " + Value.ToString() : "");
            return String.Format("{0}{1} {2} {3}", shift, Name, Type.Name, s);
        }
    }

    class SymVarArray :SymVar
    {
        public UInt64 SumLenght {get; private set;}

        public List<UInt64> Lenght { get {return ((SymTypeArray) Type).Lenght; }}


        public SymVarArray(String iName, SymTypeArray iType, bool iGlobal = false)
            : base(iName, iType, iGlobal)
        {
            SumLenght = 1;

            foreach (var len in iType.Lenght)
                SumLenght *= len;
	    }


        public override string ToString(String shift)
        {
            return String.Format("{0} size: {1}", base.ToString(shift), SumLenght);
        }
    }


    class SymFunc : Symbol, ISymbol
    {
        public SymbolTable Args {get; private set;}
        public SyntaxBlock Body { get; set; }
        public SymType RetType { get; private set; }

        public int ArgsCount { get {return Args.Vars.Count; } }
        public SymFunc (String iName, SymType iRetType, SymbolTable iArgs)
        {
            Name = iName;
            RetType = iRetType;
            Args = iArgs;
            Body = null;
        }

        public bool IsPrototipe { get { return Body == null; } }

        public override string ToString(String shift)
        {
            return String.Format("{4}Funk {0}:\n{4}{5}retType - {1}\n{4}{5}Args: \n{2}{4}{5}Body:\n{3}{4}{5}End Body\n\n{4}{5}EndFunk\n",
                Name, RetType.Name, Args.ToString(shift + OneShift), (IsPrototipe ? shift + OneShift + "Is Prototipe \n" : Body.ToString(shift + OneShift)), shift, OneShift);
        }
    }

    class SymbolTable
	{
        //в таблице храним мы переменые, типы (тайп деф и тп) функции, структура являеться типом,
       // public Int64 Size { get {return Symbols.Capacity;}  }

       // private TSymbols Symbols = new TSymbols();

        private TSymbols Types { get; set; }
        public TSymbols Vars { get; private set; }

        public TSymbols Funks { get; private set; }

        private Boolean HaveTypes { get { return Types.Count != 0; } }
        public Boolean HaveVars { get { return Vars.Count != 0; } }
        private Boolean HaveFunk { get { return Funks.Count > 1; } }

        public IEnumerable<ISymbol> Vareables { get { return Vars; } }

        public SymbolTable()
        {
		    Types = new TSymbols();
		    Vars = new TSymbols();
            Funks = new TSymbols();
        }

        public ISymbol Add(ISymbol iSymbol) 
		{
            if (iSymbol is SymType)
                Types.Add(iSymbol);
            else if (iSymbol is SymVar)
                Vars.Add(iSymbol);
            else if (iSymbol is SymFunc)
                Funks.Add(iSymbol);
            else
                throw new ComException(ERRORS.UnIdefSymbol);
            return iSymbol;
		}

        public void Copy(SymbolTable table)
        {
            Types.AddRange(table.Types);
            Vars.AddRange(table.Vars);
        }

        public string ToString(String shift)
        {
            String res = "";
            if (HaveTypes)
            {
                res += shift + "Types:\n";
                foreach (var s in Types)
                {
                    if (!(s is SymTypeInt || s is SymTypeFloat || s is SymTypeCharsConst))
                        res += s.ToString(shift + Symbol.OneShift) + "\n"; 
                }
            }
            if (HaveVars)
            {
                res += shift + "Vars:\n";
                foreach (var s in Vars)
                    res += s.ToString(shift + Symbol.OneShift) + "\n";
            }
            if (HaveFunk)
            {
                res += shift + "Funks:\n";
                foreach (var s in Funks)
                    if (((SymFunc) s).Name != "printf")
                        res += s.ToString(shift + Symbol.OneShift);
            }
            return res;
        }
		
		public SymType FindType(String iName)
		{
			foreach (var s in Types)            
                if (s.Name == iName) return (SymType)s;
			return null;
		}
		
		public SymVar FindVar(String iName)
		{
			foreach (var s in Vars)            
                if (s.Name == iName) return (SymVar)s;
			return null;
		}

        public SymFunc FindFunc(String iName)
        {
            foreach (var s in Funks)
                if (s.Name == iName) return (SymFunc)s;
            return null;
        }

		public ISymbol Find(String iName){	return FindType(iName) ?? ((ISymbol)FindVar(iName) ?? FindFunc(iName));}
	}
}
